package com.bzkj.document.utils.common;


import com.bzkj.common.core.domain.AjaxResult;
import com.bzkj.common.core.domain.model.LoginUser;
import com.bzkj.common.enums.DocumentType;
import com.bzkj.common.utils.MessageUtil;
import com.bzkj.common.utils.StringUtils;
import com.bzkj.document.domain.BaseBillDetail;
import com.bzkj.document.domain.BaseInfoMap;
import com.bzkj.document.enums.InventoryCheck;
import com.bzkj.document.exception.DocumentProcessException;
import com.bzkj.document.exception.InventoryException;
import com.bzkj.document.mapper.WmsWareHouseInventoryMapper;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @projectName: prt-wms
 * @package: com.bzkj.document.utils.documentpublic
 * @className: InventoryManagerUtils
 * @description: 库存管理工具类
 * @author: C1309
 * @createDate: 2024年12月23日
 * @version: v1.0
 */
@Component
public class InventoryManagerUtils {

    @Resource
    private WmsWareHouseInventoryMapper wmsWareHouseInventoryMapper;

    @Resource
    private MessageUtil messageUtil;

    /**
     * 常量定义
     */
    final String ERROR_CSTATEID = "库存状态不能为空！";
    final String ERROR_CPRODUCTORID = "生产厂商不能为空！";
    final String ERROR_DPRODUCEDATE = "生产日期不能为空！";
    final String ERROR_DVALIDATE = "有效期不能为空！";
    final String ERROR_VBATCHCODE = "批次号不能为空！";
    /**
     * 生产厂商字段校验（cproductorid）
     */
    private static final List<String> CPRODUCTORID =
            Collections.unmodifiableList(
                    Arrays.asList(
                            DocumentType.PURCHASE_INBOUND.getCode(),
                            DocumentType.OTHER_INBOUND.getCode(),
                            DocumentType.FINISHED_PRODUCT_INBOUND.getCode(),
                            DocumentType.OTHER_OUTBOUND.getCode(),
                            DocumentType.TRANSFER_ORDER.getCode(),
                            DocumentType.MATERIAL_OUTBOUND.getCode(),
                            DocumentType.SALES_OUTBOUND.getCode()
                    )
            );
    /**
     * 生产厂家字段校验（vmanufacturer_148）
     */
    private static final List<String> V_MANUFACTURER_148 =
            Collections.unmodifiableList(
                    Arrays.asList(


                    )
            );

    /*============================================== 分割线 ==============================================*/

    /**
     * wms入库单
     *
     * @param vBillCode 单据号
     * @param billType  单据类型
     * @return 结果数量
     */
    public Integer wmsWarehouseIn(String vBillCode, String billType) {
        return wmsWareHouseInventoryMapper.wmsWarehouseIn(vBillCode, billType);
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * wms出库单
     */
    public Integer wmsWarehouseOut(String vBillCode, String billType) {
        return wmsWareHouseInventoryMapper.wmsWarehouseOut(vBillCode, billType);
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 判断库存是否充足
     *
     * @param billType   单据类型
     * @param pkMaterial 物料主键
     * @param batchNo    批次号
     * @param quantity   数量
     * @param vbillcode  单据号
     * @param cstateid   库存状态
     */
    public Integer isEnough(String vbillcode, String billType, String pkMaterial, String batchNo, Object quantity, String operType, String cstateid, String orderType, String cwarehouseid) {
        return wmsWareHouseInventoryMapper.isEnough(vbillcode, billType, pkMaterial, batchNo, quantity, operType, cstateid, orderType, cwarehouseid);
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 库存规则单位
     *
     * @param detail 单据明细信息
     * @return 单据明细
     */
    public HashMap<String, String> wmsSkuRulesUnit(Map<String, Object> detail) {

        String vbillcode = detail.get("vbillcode") == null ? "" : String.valueOf(detail.get("vbillcode"));

        String billType = detail.get("vtrantypecode") == null ? "" : String.valueOf(detail.get("vtrantypecode"));

        Map<String, Object> cmaterialvid = (Map<String, Object>) detail.get("cmaterialvid");
        String pkMaterial = Optional.ofNullable(cmaterialvid)
                .map(c -> c.get("isMap") != null && (boolean) c.get("isMap") ? c.get("pk").toString() : c.get("value").toString())
                .orElseThrow(() -> new RuntimeException("物料信息缺失！"));
        String batchNo = detail.get("vbatchcode") == null ? "" : String.valueOf(detail.get("vbatchcode"));
        Object quantity = detail.get("nshouldnum");

        Map<String, Object> cwarehouseid = (Map<String, Object>) detail.get("cwarehouseid");
        String warehouseid = Optional.ofNullable(cwarehouseid)
                .map(c -> c.get("isMap") != null && (boolean) c.get("isMap") ? c.get("pk").toString() : c.get("value").toString())
                .orElseThrow(() -> new RuntimeException("仓库信息缺失！"));

        String cstateid = detail.get("cstateid") == null ? "" : String.valueOf(detail.get("cstateid"));
        return wmsWareHouseInventoryMapper.wmsSkuRulesUnit(vbillcode, billType,
                pkMaterial, batchNo, quantity, warehouseid, cstateid);
    }

    /**
     * 根据物料主键分配仓库
     *
     * @param pk          物料主键
     * @param warehousePk 仓库主键
     * @return 仓库集合
     */
    public List<HashMap<String, String>> selectNccWarehouse(String pk, String warehousePk) {
        return wmsWareHouseInventoryMapper.selectNccWarehouse(pk, warehousePk);
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 库存规则单位
     * 转库、移库时不传单据号传转库类型
     *
     * @param details 单据明细信息
     */
    public void wmsSkuRulesUnitTansBill(List<Map<String, Object>> details, String transType, int confirmType) {
        for (Map<String, Object> detail : details) {
            String vbillcode = detail.get("vbillcode") == null ? "" : String.valueOf(detail.get("vbillcode"));

            String billType = detail.get("vtrantypecode") == null ? "" : String.valueOf(detail.get("vtrantypecode"));

            Map<String, Object> cmaterialvid = (Map<String, Object>) detail.get("cmaterialvid");
            String pkMaterial = Optional.ofNullable(cmaterialvid)
                    .map(c -> c.get("isMap") != null && (boolean) c.get("isMap") ? c.get("pk").toString() : c.get("value").toString())
                    .orElseThrow(() -> new RuntimeException("物料信息缺失！"));
            String batchNo = detail.get("vbatchcode") == null ? "" : String.valueOf(detail.get("vbatchcode"));
            // 单据中应发数量
            Object nshouldnumObj = detail.get("nnum");
            double nshouldnum = 0;
            if (nshouldnumObj != null) {
                nshouldnum = Double.parseDouble(nshouldnumObj.toString());
            }
            // 单据中的请领数量
            Object vbdef1 = detail.get("vbdef1");
            double vbdef1Num = 0;
            if (vbdef1 != null) {
                vbdef1Num = Double.parseDouble(vbdef1.toString());
            }
            double queryNum = Math.max(vbdef1Num, nshouldnum);


            Map<String, Object> cwarehouseid = (Map<String, Object>) detail.get("cwarehouseid");
            String warehouseid = Optional.ofNullable(cwarehouseid)
                    .map(c -> c.get("isMap") != null && (boolean) c.get("isMap") ? c.get("pk").toString() : c.get("value").toString())
                    .orElseThrow(() -> new RuntimeException("仓库信息缺失！"));

            Map<String, Object> cotherwhid = (Map<String, Object>) detail.get("cotherwhid");
            String otherwhid = Optional.ofNullable(cotherwhid)
                    .map(c -> c.get("isMap") != null && (boolean) c.get("isMap") ? c.get("pk").toString() : c.get("value").toString())
                    .orElseThrow(() -> new RuntimeException("仓库信息缺失！"));


            String cstateid = detail.get("cstateid") == null ? "" : String.valueOf(detail.get("cstateid"));

            // 注释：目前转库不判断最优批号
            switch (transType) {
                case "TransferWareHouseOut": {
                    HashMap<String, String> resultMap = wmsWareHouseInventoryMapper.wmsSkuRulesUnit(transType, billType,
                            pkMaterial, batchNo, queryNum, warehouseid, cstateid);
                    detail.put("Sku_Unit_Code", resultMap.get("Sku_Unit_Code"));
                    detail.put("WarehouseMation", resultMap.get("WarehouseMation"));
                    break;
                }
                case "TransferWareHouseIn": {
                    HashMap<String, String> resultMap = wmsWareHouseInventoryMapper.wmsSkuRulesUnit(transType, billType,
                            pkMaterial, batchNo, queryNum, otherwhid, cstateid);
                    detail.put("Sku_Unit_Code", resultMap.get("Sku_Unit_Code"));
                    detail.put("WarehouseMation", resultMap.get("WarehouseMation"));
                    break;
                }
                case "TransferWareHouseMove":
                    // otherwhid 入库仓库
                    HashMap<String, String> otherwhidMap = wmsWareHouseInventoryMapper.wmsSkuRulesUnit(transType, billType,
                            pkMaterial, batchNo, queryNum, otherwhid, cstateid);
                    // cwarehouseid 出库仓库
                    HashMap<String, String> warehouseidMap = wmsWareHouseInventoryMapper.wmsSkuRulesUnit(transType, billType,
                            pkMaterial, batchNo, queryNum, warehouseid, cstateid);
                    detail.put("Sku_Unit_Code", warehouseidMap.get("Sku_Unit_Code"));
                    detail.put("New_Unit_Code", otherwhidMap.get("Sku_Unit_Code"));
                    detail.put("WarehouseMation", otherwhidMap.get("WarehouseMation"));
                    break;
            }


        }

    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 校验明细数据
     */
    public String checkErrorMsg(List<Map<String, Object>> detailsList) {
        StringBuilder errorMsgBuilder = new StringBuilder();
        for (Map<String, Object> detail : detailsList) {
            String errorMsg = checkDocConfirm(detail);
            if (errorMsg != null) {
                if (errorMsgBuilder.toString().isEmpty()) {
                    errorMsgBuilder.append("<ul>");
                }
                errorMsgBuilder.append("<li>").append(errorMsg).append("</li>");
            }
        }
        return errorMsgBuilder.toString().isEmpty() ? null : errorMsgBuilder.append("</ul>").toString();
    }


    /**
     * 单据确认时校验明细中必要字段校验
     *
     * @param requestBody 请求体
     * @return 校验结果
     */
    private String checkDocConfirm(Map<String, Object> requestBody) {
        List<String> errorList = new ArrayList<>();
        String crowno = String.valueOf(requestBody.get("crowno"));
        StringBuilder errorMessage = new StringBuilder();

        // 获取 cmaterialvid 并校验
        @SuppressWarnings("unchecked")
        HashMap<String, Object> cmaterialvid = (HashMap<String, Object>) requestBody.get("cmaterialvid");
        if (cmaterialvid != null) {
            String pk = getPkFromTargetMap(cmaterialvid);
            HashMap<String, Object> additionalMap = wmsWareHouseInventoryMapper.selectAdditionalManagement(pk);

            if (additionalMap != null && !additionalMap.isEmpty()) {
                // 校验生产日期、有效期
                validateField(additionalMap, "qualitymanflag", requestBody, errorList, ERROR_DPRODUCEDATE, "dproducedate");
                validateField(additionalMap, "qualitymanflag", requestBody, errorList, ERROR_DVALIDATE, "dvalidate");

                // 校验批次管理
                validateField(additionalMap, "wholemanaflag", requestBody, errorList, ERROR_VBATCHCODE, "vbatchcode");

                // 根据 code 校验不同字段
                // 1:校验库存状态；2:校验生产厂商；3:校验生产厂商和库存状态
                String code = String.valueOf(additionalMap.get("code"));
                switch (code) {
                    case "1":
                        validateField(requestBody, errorList, ERROR_CSTATEID, "cstateid");
                        break;
                    case "2":
                        validateField(requestBody, errorList, ERROR_CPRODUCTORID, "cproductorid");
                        break;
                    case "3":
                        validateField(requestBody, errorList, ERROR_CSTATEID, "cstateid");
                        validateField(requestBody, errorList, ERROR_CPRODUCTORID, "cproductorid");
                        break;
                    default:
                        break;
                }
            }
        }

        // 拼接错误信息
        if (!errorList.isEmpty()) {
            errorList.forEach(error -> errorMessage.append(error).append("；"));
        }

        return errorMessage.toString().trim().isEmpty() ? null : "行号：" + crowno + "-->" + errorMessage.toString().trim();
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 校验批次号是否为空
     *
     * @param details  明细列表
     * @param billType 入库or出库
     * @return 错误信息
     */
    public String checkBatchNo(List<Map<String, Object>> details, int confirmType, String billType) {
        StringBuilder stringBuffer = new StringBuilder(1024);
        boolean isFirstItem = true;

        for (Map<String, Object> detail : details) {
            Object nshouldnumObj = detail.get("nshouldnum");
            double checkNumber = Double.parseDouble(nshouldnumObj.toString());

            Map<String, String> resultMap = wmsSkuRulesUnit(detail);
            String vbatchcode = detail.get("vbatchcode") == null ? null : String.valueOf(detail.get("vbatchcode"));
            String batchNo = String.valueOf(resultMap.get("BatchNo"));

            boolean isInboundNegative = billType.equals("warehousein") && checkNumber < 0;
            boolean isOutboundPositive = billType.equals("warehouseout") && checkNumber > 0;
            boolean needCheck = (isInboundNegative || isOutboundPositive) && confirmType == 0;
            boolean hasError = false;

            if (needCheck && !Objects.equals(batchNo, vbatchcode)) {
                String pkMaterial;
                try {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> cmaterialvid = (Map<String, Object>) detail.get("cmaterialvid");
                    pkMaterial = Optional.ofNullable(cmaterialvid)
                            .map(c -> Boolean.TRUE.equals(c.get("isMap")) ? c.get("name").toString() : c.get("value").toString())
                            .orElseThrow(() -> new DocumentProcessException("单据中存在物料信息缺失，请核对后重试！"));
                } catch (Exception e) {
                    throw new DocumentProcessException(e.getMessage(), e);
                }

                if (isFirstItem) {
                    stringBuffer.append("<ul>");
                    isFirstItem = false;
                }

                String errorMessage = vbatchcode == null
                        ? String.format("<li>行号：<b>%s</b>，物料：<b>%s</b>，当前物料无批次号，需确认是否为批次管理物料！</li>",
                        detail.get("crowno"), pkMaterial)
                        : String.format("<li>行号：<b>%s</b>，物料：<b>%s</b>单据批次：<b>%s</b>，但当前可出库的最优批次：<b>%s</b></li>",
                        detail.get("crowno"), pkMaterial, vbatchcode, batchNo);
                stringBuffer.append(errorMessage);
                hasError = true;
            }

            if (!hasError) {
                detail.put("Sku_Unit_Code", resultMap.get("Sku_Unit_Code"));
                detail.put("WarehouseMation", resultMap.get("WarehouseMation"));
            }
        }

        if (!isFirstItem) {
            stringBuffer.append("</ul>");
        }

        return isFirstItem ? null : stringBuffer.toString();
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 校验库存是否充足（单据确认时校验）
     *
     * @param details       明细列表
     * @param billType      入库or出库
     * @param vtrantypecode 单据操作类型
     * @param orderType     单据类型
     * @return 校验结果
     */
    public boolean verifyInventoryConfirm(List<Map<String, Object>> details, String billType, String vtrantypecode, String orderType) {
        if (details == null || details.isEmpty()) {
            throw new InventoryException("出入库单明细为空，请核对后重试！");
        }
        // 拼接错误信息
        StringBuilder sb = new StringBuilder();
        for (Map<String, Object> detail : details) {
            // 获取明细中的物料主键
            String cmaterialvidPk = detail.get("cmaterialvid") == null ? "" : getPkFromTargetMap((HashMap<String, Object>) detail.get("cmaterialvid"));

            // 获取明细中的物料名称
            String cmaterialvidName = detail.get("cmaterialvid") == null ? "" : getNameFromTargetMap((HashMap<String, Object>) detail.get("cmaterialvid"));

            // 获取明细中的批号
            String vbatchcode = detail.get("vbatchcode") == null ? "" : detail.get("vbatchcode").toString();
            vbatchcode = vbatchcode == null ? "" : vbatchcode;

            // 获取明细中的数量
            Object nshouldnumObj = detail.get("nshouldnum");
            double checkNumber = Double.parseDouble(nshouldnumObj.toString());

            // 获取明细中的库存状态
            String cstateid = detail.get("cstateid") == null ? "" : detail.get("cstateid").toString();

            // 获取明细中的仓库信息
            String cwarehouseid = detail.get("cwarehouseid") == null ? "" : getPkFromTargetMap((HashMap<String, Object>) detail.get("cwarehouseid"));

            // 获取明细中的生产厂商信息
            String cproductorid = "";
            if (CPRODUCTORID.contains(orderType)) {
                cproductorid = detail.get("cproductorid") == null ? "" : getPkFromTargetMap((HashMap<String, Object>) detail.get("cproductorid"));
            } else if (V_MANUFACTURER_148.contains(orderType)) {
                cproductorid = detail.get("vmanufacturer_148") == null ? "" : getPkFromTargetMap((HashMap<String, Object>) detail.get("vmanufacturer_148"));
            }


            switch (billType) {
                case "warehousein":
                    if (checkNumber < 0) {
                        int enough = wmsWareHouseInventoryMapper.isEnough("", vtrantypecode, cmaterialvidPk, vbatchcode, checkNumber, "1", cstateid, cproductorid, cwarehouseid);
                        if (enough == InventoryCheck.NO_ENOUGH.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.get("crowno")).append("-->").append(cmaterialvidName).append("库存可用量不足！");
                        } else if (enough == InventoryCheck.NOT_ALLOWED.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.get("crowno")).append("-->").append(cmaterialvidName).append("：当前商品不支持在此单据类型下使用！");
                        }
                    }
                    break;
                case "warehouseout":
                    if (checkNumber > 0) {
                        int enough = wmsWareHouseInventoryMapper.isEnough("", vtrantypecode, cmaterialvidPk, vbatchcode, checkNumber, "1", cstateid, cproductorid, cwarehouseid);
                        if (enough == InventoryCheck.NO_ENOUGH.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.get("crowno")).append("-->").append(cmaterialvidName).append("库存可用量不足！");
                        } else if (enough == InventoryCheck.NOT_ALLOWED.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.get("crowno")).append("-->").append(cmaterialvidName).append("：当前商品不支持在此单据类型下使用！");
                        }
                    }
                    break;
                default:
                    throw new InventoryException("未知单据操作类型！");
            }
        }
        if (!sb.toString().trim().isEmpty()) {
            throw new InventoryException(sb.toString().trim());
        }
        return true;
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 校验库存是否充足（单据签字时校验）
     *
     * @param details  明细列表
     * @param billType 入库or出库
     * @return 校验结果
     */
    public boolean verifyInventorySign(List<? extends BaseBillDetail> details, String billType,
                                       String vtrantypecode, String orderType) {
        if (details == null || details.isEmpty()) {
            throw new InventoryException("出入库单明细为空，请核对后重试！");
        }
        // 拼接错误信息
        StringBuilder sb = new StringBuilder();
        for (BaseBillDetail detail : details) {

            if (detail == null || detail.getNnum() == null || detail.getNnum() == 0) {
                throw new InventoryException(messageUtil.updateDocumentZeroQuantityMessage(""));
            }

            // 获取明细中的物料明细信息
            BaseInfoMap cmaterialvid = detail.getCmaterialvid();

            // 获取明细中的批次信息
            String vbatchcode = detail.getVbatchcode() == null ? "" : detail.getVbatchcode();

            // 获取明细中的库存状态
            String cstateid = detail.getCstateid() == null ? "" : detail.getCstateid();

            // 获取明细中的仓库信息
            BaseInfoMap cwarehouseid = detail.getCwarehouseid();

            String cproductorid = "";
            if (CPRODUCTORID.contains(orderType)) {
                cproductorid = detail.getCproductorid() == null ? "" : detail.getCproductorid().getPk();
            } else if (V_MANUFACTURER_148.contains(orderType)) {
                cproductorid = detail.getVmanufacturer148() == null ? "" : detail.getVmanufacturer148().getPk();
            }
            switch (billType) {
                case "warehousein":
                    if (detail.getNshouldnum() < 0) {
                        int enough = wmsWareHouseInventoryMapper.isEnough(detail.getVbillcode(), vtrantypecode, cmaterialvid.getPk(), vbatchcode, detail.getNnum(), "2", cstateid, cproductorid, cwarehouseid.getPk());
                        if (enough == InventoryCheck.NO_ENOUGH.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.getCrowno()).append("-->").append(cmaterialvid.getName()).append("库存可用量不足！");
                        } else if (enough == InventoryCheck.NOT_ALLOWED.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.getCrowno()).append("-->").append(cmaterialvid.getName()).append("：当前商品不支持在此单据类型下使用！");
                        }
                    }
                    break;
                case "warehouseout":
                    if (detail.getNshouldnum() > 0) {
                        int enough = wmsWareHouseInventoryMapper.isEnough(detail.getVbillcode(), vtrantypecode, cmaterialvid.getPk(), vbatchcode, detail.getNnum(), "2", cstateid, cproductorid, cwarehouseid.getPk());
                        if (enough == InventoryCheck.NO_ENOUGH.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.getCrowno()).append("-->").append(cmaterialvid.getName()).append("库存可用量不足！");
                        } else if (enough == InventoryCheck.NOT_ALLOWED.ordinal()) {
                            sb.append("<br>").append("行号：").append(detail.getCrowno()).append("-->").append(cmaterialvid.getName()).append("：当前商品不支持在此单据类型下使用！");
                        }
                    }
                    break;
                default:
                    throw new InventoryException("未知单据操作类型！");
            }
        }
        if (!sb.toString().trim().isEmpty()) {
            throw new InventoryException(sb.toString().trim());
        }
        return true;
    }

    /*============================================== 分割线 ==============================================*/

    /**
     * 提取明细表数据并添加登录人信息
     *
     * @param targetMap 目标对象
     * @param loginUser 登录用户
     * @return 明细列表
     */
    public List<Map<String, Object>> extractDetailsList(Map<String, Object> targetMap, LoginUser loginUser) {
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> detailsList = (List<Map<String, Object>>) targetMap.get("details");
        for (Map<String, Object> detailMap : detailsList) {
            DataProcessUtils.addDetailInfo(detailMap, loginUser);
        }
        return detailsList;
    }


    /**
     * 工具方法：判断对象是否为空或空字符串
     */
    private boolean isNullOrEmpty(Object obj) {
        return obj == null || obj.toString().trim().isEmpty();
    }

    /**
     * 工具方法：从 map 中获取主键
     */
    public String getPkFromTargetMap(HashMap<String, Object> targetMap) {
        return Boolean.TRUE.equals(targetMap.getOrDefault("isMap", false)) ?
                String.valueOf(targetMap.get("pk")) :
                String.valueOf(targetMap.get("value"));
    }

    /**
     * 工具方法：从 map 中获取主键
     */
    private String getNameFromTargetMap(HashMap<String, Object> targetMap) {
        return Boolean.TRUE.equals(targetMap.getOrDefault("isMap", false)) ?
                String.valueOf(targetMap.get("name")) :
                String.valueOf(targetMap.get("value"));
    }

    /**
     * 工具方法：校验字段是否为空
     */
    private void validateField(Map<String, Object> requestBody, List<String> errorList, String errorMessage, String fieldName) {
        if (isNullOrEmpty(requestBody.get(fieldName))) {
            errorList.add(errorMessage);
        }
    }

    /**
     * 工具方法：根据管理标志校验字段
     */
    private void validateField(Map<String, Object> additionalMap, String flagKey, Map<String, Object> requestBody, List<String> errorList, String errorMessage, String fieldName) {
        if (isManagementEnabled(additionalMap, flagKey) && isNullOrEmpty(requestBody.get(fieldName))) {
            errorList.add(errorMessage);
        }
    }

    /**
     * 工具方法：判断管理是否启用
     */
    private boolean isManagementEnabled(Map<String, Object> additionalMap, String flagKey) {
        return additionalMap.get(flagKey) == null || "Y".equals(String.valueOf(additionalMap.get(flagKey)));
    }

    /**
     * 工具方法：校验批次号唯一性
     *
     * @param batchNo 批次号
     * @return 批次号唯一性校验结果
     */
    public AjaxResult checkBatchNoUnique(String batchNo) {
        if (batchNo == null) {
            return AjaxResult.error("批次号不能为空！");
        }
        if (batchNo.trim().isEmpty()) {
            return AjaxResult.success(false);
        }
        return AjaxResult.success(wmsWareHouseInventoryMapper.checkBatchNoUnique(batchNo) == 1);
    }

    /**
     * 修改货位
     *
     * @param vbillcode 单据号：（CR:采购入；TR：其他入；PR：成品入；XC:销售出；CC：材料出；TC：其他出；ZK:转库）
     * @param freight   货位
     * @return 修改结果
     */
    public AjaxResult changeFreight(String vbillcode, String freight, String cgeneralbid) {
        // 参数校验
        if (StringUtils.isBlank(vbillcode)) {
            return AjaxResult.error("单据号不能为空！");
        }
        if (StringUtils.isBlank(cgeneralbid)) {
            return AjaxResult.error("表体主键不能为空！");
        }
        if (StringUtils.isBlank(freight)) {
            return AjaxResult.error("货位不能为空！");
        }
        if (!vbillcode.matches("[A-Z]{2}.*")) {
            return AjaxResult.error("单据号必须以两个大写字母开头！");
        }

        try {
            int affectedRows;

            // 处理转库单(ZK开头)的特殊逻辑
            if (vbillcode.startsWith("ZK")) {
                String transType = wmsWareHouseInventoryMapper.getTransType(vbillcode);
                if (transType == null) {
                    return AjaxResult.error("转库单不存在或无法获取转库类型！");
                }

                String unitCodeField;
                switch (transType) {
                    case "TransferWareHouseOut":
                    case "TransferWareHouseIn":
                        unitCodeField = "Sku_Unit_Code";
                        break;
                    case "TransferWareHouseMove":
                        unitCodeField = "New_Unit_Code";
                        break;
                    default:
                        return AjaxResult.error("不支持的转库类型：" + transType);
                }

                affectedRows = wmsWareHouseInventoryMapper.changeZkFreight(
                        vbillcode, freight, cgeneralbid, unitCodeField);
            } else {
                // 处理普通单据
                String tableName = selectTableName(vbillcode);
                affectedRows = wmsWareHouseInventoryMapper.changeFreight(
                        vbillcode, tableName, freight, cgeneralbid);
            }

            // 处理执行结果
            return affectedRows >= 1
                    ? AjaxResult.success("货位修改成功！")
                    : AjaxResult.error("货位修改失败，请检查单据状态或联系管理员！");

        } catch (Exception e) {
//            log.error("修改运费异常 单据号：{} 错误：{}", vbillcode, e.getMessage());
            return AjaxResult.error("系统异常，修改货位失败：" + e.getMessage());
        }
    }

    /**
     * 校验分配的货位是否可以用
     *
     * @param warehouse 仓库PK
     * @param freight   货位
     * @return 货位是否可以用
     */
    public AjaxResult checkFreight(String warehouse, String freight) {
        List<HashMap<String, String>> list = wmsWareHouseInventoryMapper.checkFreight(warehouse, freight);
        return AjaxResult.success(list);
    }

    /**
     * 修改签收时间
     * @param targetMap 目标数据
     * @return 修改结果
     */
    public AjaxResult changeSignTime(Map<String, Object> targetMap) {
        SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (targetMap == null){
            return AjaxResult.error("请输入需要修改的单据和签字时间！");
        }
        if (targetMap.get("vbillcode").toString().isEmpty() || targetMap.get("signTime").toString().isEmpty() || targetMap.get("cgeneralhid").toString().isEmpty()){
            return AjaxResult.error("请输入需要修改的单据和签字时间！");
        }
        String signTime = targetMap.get("signTime").toString();
        String pk = targetMap.get("vbillcode").toString().startsWith("ZK") ? "cspecialhid" : "cgeneralhid"  ;

        try {
            Date date = inputFormat.parse(signTime);
            String outputTime = inputFormat.format(date);
            return wmsWareHouseInventoryMapper.changeSignTime(targetMap.get("vbillcode").toString(),
                    targetMap.get("cgeneralhid").toString(),
                    outputTime, targetMap.get("updateBy").toString(),
                    selectHeaderTableName(targetMap.get("vbillcode").toString()),
                    pk) > 0
                    ? AjaxResult.success("修改签字时间成功！")
                    : AjaxResult.error("修改签字时间失败！");
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 辅助方法：根据单据号查询表名
     *
     * @param vbillcode 单据号
     * @return 表名
     */
    private String selectTableName(String vbillcode) {
        String prefix = vbillcode.substring(0, 2);
        switch (prefix) {
            case "CR":
                return "wms_warehouse_in_detail_material";
            case "TR":
                return "wms_warehouse_in_detail_other";
            case "PR":
                return "wms_warehouse_in_detail_product";
            case "XC":
                return "wms_warehouse_out_detail_product";
            case "CC":
                return "wms_warehouse_out_detail_material";
            case "TC":
                return "wms_warehouse_out_detail_other";
            case "ZK":
                return "wms_movement_order_detail";
            default:
                return "";
        }
    }

    private String selectHeaderTableName(String vbillcode) {
        String prefix = vbillcode.substring(0, 2);
        switch (prefix) {
            case "CR":
                return "wms_warehouse_in_material";
            case "TR":
                return "wms_warehouse_in_other";
            case "PR":
                return "wms_warehouse_in_product";
            case "XC":
                return "wms_warehouse_out_product";
            case "CC":
                return "wms_warehouse_out_material";
            case "TC":
                return "wms_warehouse_out_other";
            case "ZK":
                return "wms_movement_order";
            default:
                return "";
        }
    }

}
